/********************************************************************
    copyright (c) by tnmujoy
    @created:	10/11/2011   15:12
    @filename: 	Strategy
    @author:	Limiko Lau (limiko.lau@gmail.com)
    @brief:	    
*********************************************************************/

#ifndef TNMUJOY_GDUT_DESIGNPATTERN_STRATEGY_H_
#define TNMUJOY_GDUT_DESIGNPATTERN_STRATEGY_H_

namespace tnmujoy
{
namespace gdut
{

template<class T> class StrategyManagerPrivate;
class StrategyClassInfoPrivate;
template<class T> class StrategyClassCreatorPrivate;

template<class T>
class StrategyContext
{
public:
    static std::string strategiesToString()
    {
        return StrategyManagerPrivate<T>::strategiesToString();
    }

    int useStrategy(std::string _strategyName)
    {
        std::map<std::string, std::string>& strategiesAlias = 
            StrategyManagerPrivate<T>::getStrategiesAliasTable();
        std::map<std::string, std::string>::iterator itrAlias = strategiesAlias.find(_strategyName);
        if (itrAlias != strategiesAlias.end())
        {
            std::map<std::string, StrategyClassInfoPrivate>& strategiesClass = 
                StrategyManagerPrivate<T>::getStrategiesClassTable();
            std::map<std::string, StrategyClassInfoPrivate>::iterator itrClass = strategiesClass.find(itrAlias->second);
            if (itrClass!=strategiesClass.end())
            {
                usedStrategyName_ = _strategyName;
                strategy_ =  static_cast<T*>((itrClass->second).createObject());
                return 0;
            }
        }

        assert(false);
        errorString_ = "Invalid strategy's name : " + _strategyName;
        return -1;
    }

    template<class K>
    void useStrategy()
    {
       if (strategy_)
       {
           delete strategy_;
       }
       strategy_ = new K();
    }

    std::string getUsedStrategyName()
    {
        return usedStrategyName_;
    }

    void setStrategy(T* _strategy)
    {
        strategy_ = _strategy;
    }

protected:
    StrategyContext()
        : strategy_(0)
    {

    }
    virtual ~StrategyContext()
    {
        if (strategy_)
        {
            delete strategy_;
            strategy_ = 0;
        }
    }
    T* strategy_;
private:
    std::string usedStrategyName_;
    std::string errorString_;
};

class StrategyClassInfoPrivate
{
    template<class T> friend class StrategyManagerPrivate;
    template<class T> friend class StrategyContext;
private:
    void* (*createObject)();
};

template<class T>
class StrategyClassCreatorPrivate
{
    template<class T> friend class StrategyManagerPrivate;
private:
    static void* create()
    {
        return static_cast<void*>(new T());
    }
};

template <class T>
class StrategyManagerPrivate
{
    template< typename T > friend class StrategyContext;
public:
    template<class K>
    static void registerStrategy(K* _class, std::string _className, std::string _alias)
    {
        StrategyClassInfoPrivate classInfo;
        classInfo.createObject = StrategyClassCreatorPrivate<K>::create;
        getStrategiesClassTable().insert(make_pair(_className, classInfo));
        getStrategiesAliasTable().insert(make_pair(_alias, _className));
    }
private:
    static std::map<std::string, StrategyClassInfoPrivate>& getStrategiesClassTable()
    {
        static std::map<std::string, StrategyClassInfoPrivate> classMap_;
        return classMap_;
    }
    static std::map<std::string, std::string>& getStrategiesAliasTable()
    {
        static std::map<std::string, std::string> aliasMap_;
        return aliasMap_;
    }
    static std::string strategiesToString()
    {
        std::string names;
        std::map<std::string, StrategyClassInfoPrivate>& productClassMap = 
            getStrategiesClassTable();
        std::map<std::string, StrategyClassInfoPrivate>::const_iterator citr = 
            productClassMap.begin();
        for(; citr != productClassMap.end(); ++citr)
        {
            names.append(citr->first);
            names.append(" ");
        }
        return names;
    }
};

}//namespace gdut
}//namespace tnmujoy

#define REGISTER_STRATEGY_CLASS(pBaseClass, pClass)    \
    class strategy_##pClass \
    { \
    public: \
        strategy_##pClass() \
    { \
        tnmujoy::gdut::StrategyManagerPrivate<pBaseClass>::registerStrategy((pClass*)0, #pClass, #pClass); \
    } \
    }; \
    static strategy_##pClass sStrategy_##pClass;

#define REGISTER_ALIAS_PRODUCT_CLASS(pBaseClass, pClass, alias)    \
    class strategy_##pClass \
    { \
    public: \
        strategy_##pClass() \
    { \
        tnmujoy::gdut::StrategyManagerPrivate<pBaseClass>::registerStrategy((pClass*)0, #pClass, alias); \
    } \
    }; \
    static strategy_##pClass sStrategy_##pClass;


#endif //TNMUJOY_GDUT_DESIGNPATTERN_STRATEGY_H_

//
//History:
//[10/11/2011] <limiko.lau@gmail.com> created